package com.lx.iccp.activity.app.service.impl;

import com.lx.iccp.activity.app.image.CustomProcessDiagramGenerator;
import com.lx.iccp.activity.app.service.ProcessImageService;
import org.activiti.bpmn.model.BaseElement;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.Execution;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author thadYo
 * @date 2021/11/10 10:00
 **/
@Service
public class ProcessImageServiceImpl implements ProcessImageService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Override
    public InputStream getProcImgByProcId(String procInstId) throws Exception {
        // 获取流程定义Model对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procInstId);
        // 定义流程画布生成器
        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        return generator.generateDiagram(bpmnModel,
                new ArrayList<String>(),
                new ArrayList<String>(), "宋体", "宋体", "宋体");
    }

    @Override
    public InputStream getImgByInstanceId(String instanceId) throws Exception {
        //根据流程实例id，获取历史流程实例
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId)
                .list();
        if(CollectionUtils.isEmpty(historicProcessInstances)){
            throw new RuntimeException("没有流程实例");
        }
        HistoricProcessInstance instance = historicProcessInstances.get(0);
        // 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instanceId)
                .orderByHistoricActivityInstanceId()
                .asc()
                .list();
        // 将已经执行的节点放入高亮显示节点集合
        List<String> highLightedActivityIdList = historicActivityInstanceList.stream()
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());
        List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery()
                .processInstanceId(instanceId)
                .list();
        List<String> runningActivityIdList = new ArrayList<>();
        for (Execution execution : runningActivityInstanceList) {
            if (!StringUtils.isEmpty(execution.getActivityId())) {
                runningActivityIdList.add(execution.getActivityId());
            }
        }
        // 定义流程画布生成器
        CustomProcessDiagramGenerator processDiagramGenerator = new CustomProcessDiagramGenerator();
        // 获取流程定义Model对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
        // 获取已经流经的流程线，需要高亮显示流程已经发生流转的线id集合
        List<String> highLightedFlowsIds = getHighLightedFlowsByIncomingFlows(bpmnModel, historicActivityInstanceList);
        // 根据runningActivityIdList获取runningActivityFlowsIds
        List<String> runningActivityFlowsIds = getRunningActivityFlowsIdsByIncomingFlows(bpmnModel, runningActivityIdList, historicActivityInstanceList);
        // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
        InputStream imageStream = processDiagramGenerator.generateDiagramCustom(
                bpmnModel,
                highLightedActivityIdList, runningActivityIdList, highLightedFlowsIds, runningActivityFlowsIds,
                "宋体", "微软雅黑", "黑体");
        return imageStream;
    }

    @Override
    public InputStream getImgByMyPassed(String instanceId, String userId) throws Exception {
//        //根据流程实例id，获取历史流程实例
//        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
//                .processInstanceId(instanceId)
//                .list();
//        if(CollectionUtils.isEmpty(historicProcessInstances)){
//            throw new RuntimeException("没有流程实例");
//        }
//        HistoricProcessInstance instance = historicProcessInstances.get(0);
//        //当前用户已完成的任务
//        // 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
//        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
//                .processInstanceId(instanceId)
//                .list();
//        //找出当前用户操作的节点
//        List<String> ido = new ArrayList<>();
//        for(HistoricActivityInstance historicActivityInstance : historicActivityInstanceList){
//            if(userId.equals(historicActivityInstance.getAssignee())){
//                ido.add(historicActivityInstance.getActivityId());
//            }
//        }
//        // 获取流程定义Model对象
//        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
//        // 定义流程画布生成器
//        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
//        return generator.generateDiagram(bpmnModel,
//                ido,
//                new ArrayList<String>(), "宋体", "宋体", "宋体");
        //根据流程实例id，获取历史流程实例
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId)
                .list();
        if(CollectionUtils.isEmpty(historicProcessInstances)){
            throw new RuntimeException("没有流程实例");
        }
        HistoricProcessInstance instance = historicProcessInstances.get(0);
        // 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instanceId)
                .orderByHistoricActivityInstanceId()
                .asc()
                .list();
        // 将已经执行的节点放入高亮显示节点集合
        List<String> highLightedActivityIdList = new ArrayList<>();
        List<String> idoList = new ArrayList<>();
        for(HistoricActivityInstance historicActivityInstance : historicActivityInstanceList){
            if(userId.equals(historicActivityInstance.getAssignee())){
                idoList.add(historicActivityInstance.getActivityId());
            }
            highLightedActivityIdList.add(historicActivityInstance.getActivityId());
        }
        List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery()
                .processInstanceId(instanceId)
                .list();
        List<String> runningActivityIdList = new ArrayList<>();
        for (Execution execution : runningActivityInstanceList) {
            if (!StringUtils.isEmpty(execution.getActivityId())) {
                runningActivityIdList.add(execution.getActivityId());
            }
        }
        runningActivityIdList.removeAll(idoList);
        // 定义流程画布生成器
        CustomProcessDiagramGenerator processDiagramGenerator = new CustomProcessDiagramGenerator();
        // 获取流程定义Model对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
        // 获取已经流经的流程线，需要高亮显示流程已经发生流转的线id集合
        List<String> highLightedFlowsIds = getHighLightedFlowsByIncomingFlows(bpmnModel, historicActivityInstanceList);
        // 根据runningActivityIdList获取runningActivityFlowsIds
        List<String> runningActivityFlowsIds = getRunningActivityFlowsIdsByIncomingFlows(bpmnModel, runningActivityIdList, historicActivityInstanceList);
        // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
        InputStream imageStream = processDiagramGenerator.generateMyDiagramCustom(
                bpmnModel,
                highLightedActivityIdList, runningActivityIdList, highLightedFlowsIds, runningActivityFlowsIds, idoList,
                "宋体", "微软雅黑", "黑体");
        return imageStream;
    }

    private List<String> getHighLightedFlowsByIncomingFlows(BpmnModel bpmnModel,
                                                            List<HistoricActivityInstance> historicActivityInstanceList) {
        //historicActivityInstanceList 是 流程中已经执行的历史活动实例
        // 已经流经的顺序流，需要高亮显示
        List<String> highFlows = new ArrayList<>();

        // 全部活动节点(包括正在执行的和未执行的)
        List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();

        /*
         * 循环的目的：
         *           获取所有的历史节点FlowNode并放入allHistoricActivityNodeList
         *           获取所有确定结束了的历史节点finishedActivityInstancesList
         */
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            // 获取流程节点
            // bpmnModel.getMainProcess()获取一个Process对象
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            allHistoricActivityNodeList.add(flowNode);
        }

        FlowNode currentFlowNode;
        FlowNode targetFlowNode;
        HistoricActivityInstance currentActivityInstance;

        // 循环活动节点
        for (FlowNode flowNode : allHistoricActivityNodeList) {
            // 获取每个活动节点的输入线
            List<SequenceFlow> incomingFlows = flowNode.getIncomingFlows();

            // 循环输入线，如果输入线的源头处于全部活动节点中，则将其包含在内
            for (SequenceFlow sequenceFlow : incomingFlows) {
                if (allHistoricActivityNodeList.stream().map(BaseElement::getId).collect(Collectors.toList()).contains(sequenceFlow.getSourceFlowElement().getId())){
                    highFlows.add(sequenceFlow.getId());
                }
            }

        }

        return highFlows;
    }

    private List<String> getRunningActivityFlowsIds(BpmnModel bpmnModel, List<String> runningActivityIdList, List<HistoricActivityInstance> historicActivityInstanceList) {
        List<String> runningActivityFlowsIds = new ArrayList<>();
        List<String> runningActivityIds = new ArrayList<>(runningActivityIdList);
        // 逆序寻找，因为historicActivityInstanceList有序
        if (CollectionUtils.isEmpty(runningActivityIds)) {
            return runningActivityFlowsIds;
        }
        for (int i = historicActivityInstanceList.size() - 1; i >= 0; i--) {
            HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(i);
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            // 如果当前节点是未完成的节点
            if (runningActivityIds.contains(flowNode.getId())) {
                continue;
            }
            // 当前节点的所有流出线
            List<SequenceFlow> outgoingFlowList = flowNode.getOutgoingFlows();
            // 遍历所有的流出线
            for (SequenceFlow outgoingFlow : outgoingFlowList) {
                // 获取当前节点流程线对应的下一级节点
                FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(), true);
                // 如果找到流出线的目标是runningActivityIdList中的，那么添加后将其移除，避免找到重复的都指向runningActivityIdList的流出线
                if (runningActivityIds.contains(targetFlowNode.getId())) {
                    runningActivityFlowsIds.add(outgoingFlow.getId());
                    runningActivityIds.remove(targetFlowNode.getId());
                }
            }

        }
        return runningActivityFlowsIds;
    }

    /**
     *
     * 并行网关的流入的线不止一条，就会导致“没有完成的并行网关”的入线只有一条会成为红色，其他入线都是绿色。
     * 解决：将第二步的操作改成获得所有“活动的未完成节点”的入线，而不是原来的找所谓的“出线”
     */
    private List<String> getRunningActivityFlowsIdsByIncomingFlows(BpmnModel bpmnModel, List<String> runningActivityIdList, List<HistoricActivityInstance> historicActivityInstanceList) {
        List<String> runningActivityFlowsIds = new ArrayList<>();
        List<String> runningActivityIds = new ArrayList<>(runningActivityIdList);
        // 逆序寻找，因为historicActivityInstanceList有序
        if (CollectionUtils.isEmpty(runningActivityIds)) {
            return runningActivityFlowsIds;
        }

        // 全部活动节点(包括正在执行的和未执行的)
        List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            // 获取流程节点
            // bpmnModel.getMainProcess()获取一个Process对象
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            allHistoricActivityNodeList.add(flowNode);
        }

        for (int i = historicActivityInstanceList.size() - 1; i >= 0; i--) {
            HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(i);
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            // 如果当前节点是未完成的节点
            if (runningActivityIds.contains(flowNode.getId())) {
                // 获取每个活动节点的输入线
                List<SequenceFlow> incomingFlows = flowNode.getIncomingFlows();

                // 循环输入线，如果输入线的源头处于全部活动节点中，则将其包含在内
                for (SequenceFlow sequenceFlow : incomingFlows) {
                    if (allHistoricActivityNodeList.stream().map(BaseElement::getId).collect(Collectors.toList()).contains(sequenceFlow.getSourceFlowElement().getId())) {
                        runningActivityFlowsIds.add(sequenceFlow.getId());
                    }
                }
            }

        }
        return runningActivityFlowsIds;
    }
}
